import { SyncStorage } from "jotai/vanilla/utils/atomWithStorage"
import cbor from "cbor-js"
import { PersistStorage, StorageValue } from "zustand/middleware"
import AssetKey, { AssetKeySvg } from "custom/asset_key"
import svgMap from "@/constants/svg_map"
import { v4 as uuidv4 } from "uuid"
import { PATHS as _PATHS } from "@/constants/path"
import { PayloadFormatter } from "./format"
import ENV from "./env"
import { createLogger } from "./logger"

export const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

const logger = createLogger("utils/common")

export function encryptData(
    data: object,
    opts?: {
        _inactive?: boolean
    },
) {
    if (opts?._inactive) {
        return JSON.stringify(data)
    }
    try {
        return btoa(encodeURIComponent(JSON.stringify(data)))
    } catch (error) {
        logger.debug("encryptData error", error)
        return JSON.stringify(data)
    }
}

export function decryptData(
    data: string,
    opts?: {
        _inactive?: boolean
    },
) {
    if (opts?._inactive) {
        return JSON.parse(data)
    }
    try {
        return JSON.parse(decodeURIComponent(atob(data)))
    } catch (error) {
        logger.debug("decryptData error", error)
        return JSON.parse(data)
    }
}

export function genStorageKey(key: string) {
    const appVersion = __APP_VERSION__ ? `${__APP_VERSION__}` : "no_version"
    const _key = `atom:${appVersion}:${key}`
    return ENV.dev_mode ? _key : btoa(_key)
}

export function createSyncStorage<Value>(
    storage: Storage = localStorage,
    opts?: {
        isEncoded?: boolean
    },
) {
    const { isEncoded = true } = opts ?? {}
    const _storage = storage
    const output: SyncStorage<Value> = {
        getItem(key, initialValue) {
            const value = _storage.getItem(key)
            if (value) {
                try {
                    return isEncoded
                        ? decryptData(value, {
                              _inactive: ENV.dev_mode,
                          })
                        : JSON.parse(value)
                } catch (error) {
                    console.error(error)
                    return initialValue
                }
            }
            return initialValue
        },
        setItem(key: string, newValue: Value) {
            if (newValue === null || newValue === undefined) {
                return this.removeItem(key)
            }
            const encryption = isEncoded
                ? encryptData(newValue, {
                      _inactive: ENV.dev_mode,
                  })
                : JSON.stringify(newValue)
            _storage.setItem(key, encryption)
        },
        removeItem(key) {
            _storage.removeItem(key)
        },
    }

    return output
}

export function createPersistStorage<Value, S extends StorageValue<Value> = StorageValue<Value>>(
    storage: Storage = localStorage,
    opts?: {
        isEncoded?: boolean
    },
) {
    const { isEncoded = true } = opts ?? {}
    const _storage = storage
    const output: PersistStorage<S> = {
        getItem(name) {
            const value = _storage.getItem(name)
            if (value) {
                try {
                    return isEncoded
                        ? decryptData(value, {
                              _inactive: ENV.dev_mode,
                          })
                        : JSON.parse(value)
                } catch (error) {
                    console.error(error)
                    return null
                }
            }
            return null
        },
        setItem(name, newValue) {
            if (newValue === null || newValue === undefined) {
                return this.removeItem(name)
            }
            const encryption = isEncoded
                ? encryptData(newValue, {
                      _inactive: ENV.dev_mode,
                  })
                : JSON.stringify(newValue)
            _storage.setItem(name, encryption)
        },
        removeItem(name) {
            _storage.removeItem(name)
        },
    }

    return output
}

export function getRedirectUrl(path: string, query?: Record<string, string>) {
    const searchParams = new URLSearchParams(query)

    return {
        get value() {
            return `${location.origin}/${path}?${searchParams.toString()}`
            //不确定是否有isExportRoiBest这个版本
            // return commonConstant.isExportRoiBest
            //     ? `${location.origin}${location.pathname}#${path}?${searchParams.toString()}`
            //     : `${location.origin}/${path}?${searchParams.toString()}`
        },
        searchParams,
        toString() {
            return this.value
        },
    }
}
export const PATHS = {
    DEPOSIT_CONFIRM: "my",
}

export function getRedirectUrlToDepositConfirmPage(params: WalletType.DepositConfirmPageParams) {
    const url = getRedirectUrl(PATHS.DEPOSIT_CONFIRM)
    const searchParams = url.searchParams
    searchParams.set("payload", encryptData(params))
    return {
        get value() {
            return url.value
        },
        searchParams,
        toString() {
            return this.value
        },
    }
}

export const CBOR = {
    encode: cbor.encode,
    decode: cbor.decode,
}

const assets_path_base = "../assets/images/"

// 急加载
const assets_path_list = import.meta.glob("../assets/images/**/*.(png|jpg|jpeg|gif|webp|avif)", {
    eager: true,
    query: "?url",
})

const assets_map = Object.fromEntries(
    Object.entries(assets_path_list).map(([key, value]) => {
        const path = key.replace(assets_path_base, "")
        return [path, value]
    }),
)

export function resolveAssetImage(key: AssetKey) {
    const resolved = assets_map[key]
    // @ts-ignore
    return (resolved?.default ?? "") as string
}

export function resolveAssetSvg(key: AssetKeySvg) {
    return svgMap[key]
}

export function getCurrentPx(rem: number) {
    const screenWidth = window.innerWidth
    const px = rem * screenWidth * 0.01 * 0.26666666
    return px
}

export function resolveAssetImageToBase64(key: AssetKey) {
    const resolved = assets_map[key]
    // @ts-ignore
    const image = resolved?.default ?? ""
    return image
}

export function getBizId() {
    return uuidv4().replace(/-/g, "")
}

export function generateOcrCallbackUrl<T extends Record<string, unknown>>(payload: T) {
    const url = new URL(`${location.origin}${_PATHS.KYC}`)
    const searchParams = url.searchParams
    const payloadSerialized = PayloadFormatter.encode(payload)
    searchParams.set("payload", payloadSerialized)
    return {
        get success() {
            url.searchParams.set("success", "true")
            return url
        },
        get fail() {
            url.searchParams.set("success", "false")
            return url
        },
    }
}

export function parseOcrClientCfg(clientCfg: string) {
    try {
        const jsonObject = JSON.parse(atob(clientCfg ?? ""))
        jsonObject.content = JSON.parse(jsonObject.content)
        return jsonObject as Kyc.Data.ClientCfg
    } catch (error) {
        logger.debug("parseOcrClientCfg", error)
        return {} as Kyc.Data.ClientCfg
    }
}

export function encodeOcrClientCfg(clientCfg: Kyc.Data.ClientCfg) {
    const cfg_raw = {
        ...clientCfg,
        content: JSON.stringify(clientCfg.content),
    }
    const cfg_json_string = JSON.stringify(cfg_raw)
    const cfg_base64 = btoa(cfg_json_string)
    return cfg_base64
}

export function fileToBase64(file: File) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onloadend = () => {
            resolve(reader.result) // base64 字符串
        }
        reader.onerror = (error) => {
            reject(error)
        }
        reader.readAsDataURL(file) // 讀取圖片文件為 base64
    })
}

export function base64ToFile(base64: string, filename: string) {
    const [metadata, base64Data] = base64.split(",")
    const mimeType = metadata.match(/:(.*?);/)?.[1] ?? "" // 取得 MIME 類型
    const binaryString = atob(base64Data) // 解碼 Base64 字符串為二進位字串

    // 創建 ArrayBuffer
    const byteArray = new Uint8Array(binaryString.length)
    for (let i = 0; i < binaryString.length; i++) {
        byteArray[i] = binaryString.charCodeAt(i)
    }

    // 生成 File 物件
    return new File([byteArray], `${filename}.${mimeType.split("/")[1]}`, { type: mimeType })
}

export function urlToFile(url: string, filename: string): Promise<File> {
    if (url.startsWith("data:")) {
        const arr = url.split(",")
        const mimeMatch = arr[0].match(/:(.*?);/)
        const mime = mimeMatch ? mimeMatch[1] : ""
        const ext = mime.split("/")[1]
        const bstr = atob(arr[arr.length - 1])
        let n = bstr.length
        const u8arr = new Uint8Array(n)

        while (n--) {
            u8arr[n] = bstr.charCodeAt(n)
        }

        const file = new File([u8arr], `${filename}.${ext}`, {
            type: mime || "",
        })
        return Promise.resolve(file)
    }

    return fetch(url)
        .then((res) => res.arrayBuffer())
        .then((buf) => new File([buf], filename, { type: "application/octet-stream" }))
}
